#include "Subsystems/BlockBehaviors/SubsystemBucketBlockBehavior.h"
#include "Log.h"
#include "GameSingleton.h"
#include "Components/ComponentMiner.h"
#include "Components/ComponentOnFire.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentPlayer.h"
#include "Subsystems/SubsystemTerrain.h"

#include "GameRandom.hpp"
#include "Managers/BlocksManager.h"
#include "Game/BodyRaycastResult.hpp"

#include "Blocks/WaterBlock.h"
#include "Blocks/MagmaBlock.h"

#include "Blocks/EmptyBucketBlock.h"
#include "Blocks/WaterBucketBlock.h"
#include "Blocks/MagmaBucketBlock.h"
#include "Blocks/MilkBucketBlock.h"

#include "Blocks/RottenMilkBucketBlock.h"
#include "Blocks/PumpkinSoupBucketBlock.h"
#include "Blocks/RottenPumpkinSoupBucketBlock.h"
#include "Blocks/PaintStripperBucketBlock.h"
#include "Blocks/PaintBucketBlock.h"
#include "Blocks/IPaintableBlock.hpp"


namespace PocketSurvival
{
    SubsystemBucketBlockBehavior::SubsystemBucketBlockBehavior()
    {
        m_handleBlocks.push_back(EmptyBucketBlock::Index());
        m_handleBlocks.push_back(WaterBucketBlock::Index());
        m_handleBlocks.push_back(MagmaBucketBlock::Index());
        m_handleBlocks.push_back(MilkBucketBlock::Index());
        m_handleBlocks.push_back(RottenMilkBucketBlock::Index());
        m_handleBlocks.push_back(PumpkinSoupBucketBlock::Index());
        m_handleBlocks.push_back(RottenPumpkinSoupBucketBlock::Index());
        m_handleBlocks.push_back(PaintStripperBucketBlock::Index());
        m_handleBlocks.push_back(PaintBucketBlock::Index());
    }

    bool SubsystemBucketBlockBehavior::onUse(const Ray3 &ray, ComponentMiner *componentMiner)
    {
        IInventory *inventory = componentMiner->inventory;
        int32_t activeBlockValue = componentMiner->getActiveBlockValue();
        int32_t num = Terrain::ExtractContents(activeBlockValue);
        switch (num)
        {
        case EmptyBucketBlock::Index():
            {
                RaycastData raycastData = componentMiner->raycast(ray, RaycastMode::Gathering);
                if(raycastData.terrainRaycastResult != nullptr)
                {
                    CellFace &cellFace = raycastData.terrainRaycastResult->cellFace;
                    int32_t cellValue = m_terrain->getCellValue(cellFace.point);
                    int32_t num2 = Terrain::ExtractContents(cellValue);
                    int32_t data = Terrain::ExtractData(cellValue);
                    Block *block = GameSingleton::blocksManager->blocks[num2];
                    if (block->isBlock<WaterBlock*>() && FluidBlock::GetLevel(data) == 0)
                    {
                    	int32_t value = Terrain::ReplaceContents(activeBlockValue, WaterBucketBlock::Index());
                    	inventory->removeNetSlotItems(inventory->getActiveSlotIndex(), inventory->getSlotCount(inventory->getActiveSlotIndex()));
                    	if (inventory->getSlotCount(inventory->getActiveSlotIndex()) == 0)
                    	{
                    		inventory->addNetSlotItems(inventory->getActiveSlotIndex(), value, 1);
                    	}
                        m_terrain->destroyCell(0, cellFace.point.posX, cellFace.point.posY, cellFace.point.posZ, 0, false, componentMiner);
                        return true;
                    }
                    if (block->isBlock<MagmaBlock*>() && FluidBlock::GetLevel(data) == 0)
                    {
                    	int32_t value2 = Terrain::ReplaceContents(activeBlockValue, MagmaBucketBlock::Index());
                    	inventory->removeNetSlotItems(inventory->getActiveSlotIndex(), inventory->getSlotCount(inventory->getActiveSlotIndex()));
                    	if (inventory->getSlotCount(inventory->getActiveSlotIndex()) == 0)
                    	{
                    		inventory->addNetSlotItems(inventory->getActiveSlotIndex(), value2, 1);
                    	}
                        m_terrain->destroyCell(0, cellFace.point.posX, cellFace.point.posY, cellFace.point.posZ, 0, false, componentMiner);
                        return true;
                    }
                }
                else if(raycastData.bodyRaycastResult != nullptr)
                {
                    // 装牛奶，未完成...
                }
            }
            break;
        case WaterBucketBlock::Index():
            {
                RaycastData raycastData = componentMiner->raycast(ray, RaycastMode::Interaction);
                if(raycastData.terrainRaycastResult != nullptr && componentMiner->place(*(raycastData.terrainRaycastResult), Terrain::MakeBlockValue(WaterBlock::Index())))
                {
                    inventory->removeNetSlotItems(inventory->getActiveSlotIndex(), 1);
                    if (inventory->getSlotCount(inventory->getActiveSlotIndex()) == 0)
                    {
                    	int32_t value4 = Terrain::ReplaceContents(activeBlockValue, EmptyBucketBlock::Index());
                    	inventory->addNetSlotItems(inventory->getActiveSlotIndex(), value4, 1);
                    }
                    return true;
                }
            }
            break;
        case MagmaBucketBlock::Index():
            {
                RaycastData raycastData = componentMiner->raycast(ray, RaycastMode::Interaction);
                if(raycastData.terrainRaycastResult != nullptr && componentMiner->place(*(raycastData.terrainRaycastResult), Terrain::MakeBlockValue(MagmaBlock::Index())))
                {
                    inventory->removeNetSlotItems(inventory->getActiveSlotIndex(), 1);
                    if (inventory->getSlotCount(inventory->getActiveSlotIndex()) == 0)
                    {
                    	int32_t value4 = Terrain::ReplaceContents(activeBlockValue, EmptyBucketBlock::Index());
                    	inventory->addNetSlotItems(inventory->getActiveSlotIndex(), value4, 1);
                    }
                    return true;
                }
            }
            break;
        case MilkBucketBlock::Index():
        case RottenMilkBucketBlock::Index():
            return true;
        case PumpkinSoupBucketBlock::Index():
        case RottenPumpkinSoupBucketBlock::Index():
            return true;
        case PaintStripperBucketBlock::Index():
        case PaintBucketBlock::Index():
            {
                RaycastData raycastData = componentMiner->raycast(ray, RaycastMode::Digging);
                if(raycastData.terrainRaycastResult != nullptr)
                {
                    CellFace &cellFace = raycastData.terrainRaycastResult->cellFace;
                    int32_t cellValue = m_terrain->getCellValue(cellFace.point);
                    int32_t contents = Terrain::ExtractContents(cellValue);
                    IPaintableBlock *paintableBlock = dynamic_cast<IPaintableBlock*>(GameSingleton::blocksManager->blocks[contents]);
                    if(paintableBlock != nullptr)
                    {
                        Vector3 normal = CellFace::FaceToVector3(cellFace.face);
                        Vector3 position = raycastData.terrainRaycastResult->hitPoint();
                        int32_t num4 = (num == PaintStripperBucketBlock::Index()) ? -1 : PaintBucketBlock::GetColor(Terrain::ExtractData(activeBlockValue));
                        int value6 = paintableBlock->paint(cellValue, num4);
                        m_terrain->changeCell(cellFace.point, value6);
                        componentMiner->damageActiveTool(1);
                    }
                    return true;
                }
            }
            break;
        }
        return false;
    }

    void SubsystemBucketBlockBehavior::load(const nlohmann::json &json)
    {
        m_terrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();
    }

    static const std::string subsystemName = "BucketBlockBehavior";
    const std::string& SubsystemBucketBlockBehavior::getName() const
    {
        return subsystemName;
    }

}